package singleton;

/**
 * 双重检查锁定 （DCL,Double Check Lock)
 *
 */
public class DCLSingleton {

    private static volatile /*volatile 关键字禁止指令重排*/ DCLSingleton dclSingleton;

    private DCLSingleton(){

    }

    /**
     * 当声明变量 dclSingleton 时，如果没有加 volatile 关键字。有可能出现指令重排的问题
     * 第4步 dclSingleton = new DCLSingleton(); 在JVM看来有一下几步：
     *   ①、为对象分配内存空间
     *   ②、初始化对象
     *   ③、将 dclSingleton 引用指向第①步中分配的内存地址
     * 在以上三步中，第②步初始化对象和第③步将 dclSingleton 引用指向对象的内存地址之间可能存在重排序。就有可能出现如下表的情况
     *
     *
     *     时间               线程A                              线程B
     *     t1     A1:分配对象的内存空间
     *     t2     A3:设置 dclSingleton 指向内存空间
     *     t3                                            B1:判断 dclSingleton 是否为空
     *     t4                                            B2:由于 dclSingleton 不为空，线程B将访问 dclSingleton 指向的对象
     *     t5     A2:初始化对象
     *     t6     A3:访问 dclSingleton 引用的对象
     *
     *     由于A3 和 A2 发出重排序导致线程 B 获取了一个尚未初始化的对象。解决方式就是用 volatile 修饰 dclSingleton
     *
     * @return DCLSingleton
     */
    public static DCLSingleton getInstance(){
        if(dclSingleton==null){  //1、做第一次判断，如果已经实例化了，直接返回对象，避免无用的同步消耗
            synchronized (DCLSingleton.class){ //2、仅对实例化过程做同步操作，保证单例
                if(dclSingleton==null){ //3、做第二次判断，只有dclSingleton 为空时再初始化。
                    dclSingleton = new DCLSingleton(); //4
                }
            }
        }
        return dclSingleton;
    }
}
